/* Funky instruction set datatype contains the extension to the function
 * name which will be called (in local window[] scope) and the 'real'
 * opcode in case of substitutions, as well as max number of bytes that
 * the function takes in storage - this is important as some of the
 * opcodes (ie: JMP) assign absolute values to the program counter so
 * we can't blindly assume that we must bump the PC every time
 */
var instructionSet = new Array();

// we do a lot of these
function decode_address(addr) {
	return(parseInt(peek(addr + 1)) + parseInt((peek(addr + 2) * 10)));
}

// Break from program to monitor
instructionSet[0] = { "opcode": 0, "mnemonic": "BRK", "bytes": 0 };
function execute_BRK(bytes) {
	set_run_mode(programState.READY);
	return(bytes);
}

// Load accumulator from location xx
instructionSet[1] = { "opcode": 1, "mnemonic": "LDA", "bytes": 3 };
function execute_LDA(bytes) {
	var addr = decode_address(get_pc());
	set_accumulator(peek(addr));
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Store accumulator into location xx
instructionSet[2] = { "opcode": 2, "mnemonic": "STA", "bytes": 3 };
function execute_STA(bytes) {
	var addr = decode_address(get_pc());
	poke(addr, get_accumulator());
	return(bytes);
}

// Clear carry (to zero)
instructionSet[3] = { "opcode": 3, "mnemonic": "CLC", "bytes": 1 };
function execute_CLC(bytes) {
	set_carry_flag(0);
	return(bytes);
}

// Add contents of location xx to accumulator with carry
instructionSet[4] = { "opcode": 4, "mnemonic": "ADC", "bytes": 3 };
function execute_ADC(bytes) {
	var addr = decode_address(get_pc());
	adc_accumulator(peek(addr));
	return(bytes);
}

// Decrement the contents of location xx
instructionSet[5] = { "opcode": 5, "mnemonic": "DEC", "bytes": 3 };
function execute_DEC(bytes) {
	var addr = decode_address(get_pc());
	var value = peek(addr);
	value--;
	set_zero_flag(value);
	if (value < 0) {
		value = 9;
	}
	poke(value, addr);
	return(bytes);
}

// Increment the contents of location xx
instructionSet[6] = { "opcode": 6, "mnemonic": "INC", "bytes": 3 };
function execute_INC(bytes) {
	var addr = decode_address(get_pc());
	var value = peek(addr);
	value++;
	set_carry_flag(value);
	value = value % 10;
	set_zero_flag(value);
	poke(value, addr);
	return(bytes);
}

// Move immediate value into accumulator
instructionSet[7] = { "opcode": 7, "mnemonic": "MOV", "bytes": 2 };
function execute_MOV(bytes) {
	set_accumulator(peek(get_pc() + 1));
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Jump to location xx
instructionSet[8] = { "opcode": 8, "mnemonic": "JMP", "bytes": 3 };
function execute_JMP(bytes) {
	var addr = decode_address(get_pc());
	return(addr - get_pc());
}

// Jump to location xx if previous result != 0 (zero flag clear)
instructionSet[9] = { "opcode": 9, "mnemonic": "JNE", "bytes": 3 };
function execute_JNE(bytes) {
	if (0 == zeroFlag) {
		var addr = decode_address(get_pc());
		bytes = addr - get_pc();
	}
	return(bytes);
}

// optional replacement instructions (10-19)

// Set carry (to one)
instructionSet[10] = { "opcode": 10, "mnemonic": "SEC", "bytes": 1 };
function execute_SEC(bytes) {
	set_carry_flag(10);
	return(bytes);
}

// Subtract contents of xx from accumulator with carry
instructionSet[11] = { "opcode": 11, "mnemonic": "SBC", "bytes": 3 };
function execute_SBC(bytes) {
	var addr = decode_address(get_pc());
	var value = peek(addr);
	set_accumulator(get_accumulator() - value);
	set_accumulator(get_accumulator() - get_carry_flag());
	if (get_accumulator() < 0) {
		set_accumulator(10 + get_accumulator());
	}
	set_zero_flag(get_accumulator());
	return(bytes);
}

//Jump to location xx if carry is clear
instructionSet[12] = { "opcode": 12, "mnemonic": "JCC", "bytes": 3 };
function execute_JCC(bytes) {
	if (0 == get_carry_flag()) {
		var addr = decode_address(get_pc());
		bytes = addr - get_pc();
	}
	return(bytes);
}

// Jump to location xx if carry is set
instructionSet[13] = { "opcode": 13, "mnemonic": "JCS", "bytes": 3 };
function execute_JCS(bytes) {
	if (0 != get_carry_flag()) {
		var addr = decode_address(get_pc());
		bytes = addr - get_pc();
	}
	return(bytes);
}

// Jump to location xx if zero flag is set
instructionSet[14] = { "opcode": 14, "mnemonic": "JEQ", "bytes": 3 };
function execute_JEQ(bytes) {
	if (0 != get_zero_flag()) {
		var addr = decode_address(get_pc());
		bytes = addr - get_pc();
	}
	return(bytes);
}

// Subtract one from the accumulator
instructionSet[15] = { "opcode": 15, "mnemonic": "DCA", "bytes": 1 };
function execute_DCA(bytes) {
	set_accumulator(get_accumulator() - 1);
	if (get_accumulator() < 0) {
		set_accumulator(10 + get_accumulator());
	}
	set_zero_flag(get_pc());
	return(bytes);
}

// Add one to the accumulator
instructionSet[16] = { "opcode": 16, "mnemonic": "INA", "bytes": 1 };
function execute_INA(bytes) {
	var acc = get_accumulator();
	acc += 1;
	set_accumulator(acc);
	set_carry_flag(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Compare the accumulator with immediate value (zero flag set if true, clear otherwise)
instructionSet[17] = { "opcode": 17, "mnemonic": "TST", "bytes": 2 };
function execute_TST(bytes) {
	var value = peek(get_pc() + 1);
	set_zero_flag(get_accumulator() - value);
	return(bytes);
}

// Load accumulator with the contents pointed to by address xx and xx+1
instructionSet[18] = { "opcode": 18, "mnemonic": "LDI", "bytes": 3 };
function execute_LDI(bytes) {
	var addr = decode_address(get_pc());
	var addr2 = decode_address(addr);
	set_accumulator(peek(addr2));
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Store accumulator into the address pointed to by xx and xx+1
instructionSet[19] = { "opcode": 19, "mnemonic": "STI", "bytes": 3 };
function execute_STI(bytes) {
	var addr = decode_address(get_pc());
	var addr2 = decode_address(addr);
	set_value(addr2, get_accumulator());
	return(bytes);
}

// Poke the IO space directly
instructionSet[20] = { "opcode": 20, "mnemonic": "OUT", "bytes": 2 };
function execute_OUT(bytes) {
	var port = decode_address(get_pc());
	var value = get_accumulator();
	set_port(port, value);
	return(bytes);
}

// Peek the IO space directly
instructionSet[21] = { "opcode": 21, "mnemonic": "INB", "bytes": 2 };
function execute_INB(bytes) {
	var port = decode_address(get_pc());
	var value = get_port(port);
	set_carry_flag(value);
	set_accumulator(value);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Shift the Accumulator left one bit
instructionSet[22] = { "opcode": 22, "mnemonic": "SAL", "bytes": 1 };
function execute_SAL(bytes) {
	var acc = get_accumulator();
	acc = acc << 1;
	set_carry_flag(acc);
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Shift the Accumulator right one bit
instructionSet[23] = { "opcode": 23, "mnemonic": "SAR", "bytes": 1 };
function execute_SAR(bytes) {
	var acc = get_accumulator();
	acc = acc >> 1;
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Rotate the Accumulator left one bit through the Carry flag
instructionSet[24] = { "opcode": 24, "mnemonic": "RAL", "bytes": 1 };
function execute_RAL(bytes) {
	var acc = get_accumulator();
	acc = acc << 1;
	acc = acc | carryFlag;
	set_carry_flag(acc);
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Rotate the Accumulator right one bit through the Carry flag
instructionSet[25] = { "opcode": 25, "mnemonic": "RAR", "bytes": 1 };
function execute_RAR(bytes) {
	var acc = get_accumulator();
	acc = acc >> 1;
	acc = acc | (carryFlag * 8);
	set_carry_flag(acc);
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}
